﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;
using NExt.Handler.Base.Common.Manager;

namespace NExt.Handler.Component.Default.JsSerialize
{
    public class RemoteMethodScriptGenerator : INExtScriptGenerator
    {
        private const string TYPE_CONTENT_FORMAT = "Ext.override({0}, {{\r\n{1}\r\n}});";
        private const string CALLBACK = "callback";

        public string Build(Type type)
        {
            string body = SerializeTypeBody(type.Assembly, type);
            if (string.IsNullOrEmpty(body))
                return string.Empty;

            string name = type.FullName;
            string content = string.Format(TYPE_CONTENT_FORMAT, name, body);
            return content;
        }

        private string SerializeTypeBody(Assembly assembly, Type type)
        {
            MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            List<MethodInfo> methodInfoList = new List<MethodInfo>(methodInfos);
            List<MethodInfo> selectedMethodInfoList = methodInfoList.FindAll(IsAppropriate);

            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < selectedMethodInfoList.Count; i++)
            {
                MethodInfo methodInfo = selectedMethodInfoList[i];

                stringBuilder.Append("\t");
                string serializedMethod = SerializeMethod(assembly, type, methodInfo);
                stringBuilder.Append(serializedMethod);

                if (i == selectedMethodInfoList.Count - 1)
                    break;

                stringBuilder.AppendFormat("{0}\r\n", ',');
            }

            string content = stringBuilder.ToString();
            return content;
        }

        private bool IsAppropriate(MethodInfo methodInfo)
        {
            string name = methodInfo.Name;

            if (name.StartsWith("get_"))
                return false;

            if (name.StartsWith("set_"))
                return false;

            if (methodInfo.DeclaringType == typeof(object))
                return false;

            return true;
        }

        private string SerializeMethod(Assembly assembly, Type type, MethodInfo methodInfo)
        {
            string methodName = methodInfo.Name;
            string methodParameter = SerializeMethodParameters(type, methodInfo.GetParameters());
            if(!string.IsNullOrEmpty(methodParameter))
            {
                methodParameter += ", ";
            }
            string methodBody = SerializeMethodBody(assembly, type, methodInfo);
            string content = string.Format("{0}: function({1}{2}) {{ {3} }}", methodName, methodParameter, CALLBACK, methodBody);
            return content;
        }

        private string SerializeMethodBody(Assembly assembly, Type type, MethodInfo methodInfo)
        {
            string assemblyJsName = AssemblyManager.GetJsName(assembly);
            string typeName = type.FullName;
            string methodName = methodInfo.Name;
            string callParameters = SerializableCallParameters(type, methodInfo.GetParameters());
            string content = string.Format("return NExt.remoteInvoke({0}, '{1}', '{2}', {3}, {4});", assemblyJsName, typeName, methodName, callParameters, CALLBACK);
            return content;
        }

        private string SerializableCallParameters(Type type, ParameterInfo[] parameterInfos)
        {
            string methodParameters = SerializeMethodParameters(type, parameterInfos);
            string content = string.Format("[{0}]", methodParameters);
            return content;
        }

        private string SerializeMethodParameters(Type type, ParameterInfo[] parameterInfos)
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < parameterInfos.Length; i++)
            {
                ParameterInfo parameterInfo = parameterInfos[i];
                string parameterName = parameterInfo.Name;
                stringBuilder.Append(parameterName);
                if (i == parameterInfos.Length - 1)
                    continue;

                stringBuilder.Append(", ");
            }

            string content = stringBuilder.ToString();
            return content;
        }
    }
}
